from appium.webdriver.common.mobileby import MobileBy
from selenium.common.exceptions import TimeoutException, NoSuchElementException
from typing_extensions import Literal

from views.base_element import Button, Text, BaseElement, SilentButton
from views.base_view import BaseView, UnreadMessagesCountText


class ChatButton(Button):
    def __init__(self, driver, **kwargs):
        super().__init__(driver, **kwargs)

    def navigate(self):
        from views.chat_view import ChatView
        return ChatView(self.driver)


class ActivityTabButton(Button):
    def __init__(self, driver, **kwargs):
        super().__init__(driver, **kwargs)

    @property
    def counter(self):
        return BaseElement(self.driver,
                           xpath='//*[@content-desc="%s"]//*[@content-desc="notification-dot"]' % self.accessibility_id)


class ChatElement(SilentButton):
    def __init__(self, driver, username_part, community=False, community_channel=False):
        self.username = username_part
        self.community = community
        self.community_channel = community_channel
        if self.community_channel:
            super().__init__(
                driver,
                xpath="//*[@content-desc='channel-list-item']//*[starts-with(@text,'# %s')]/.." % username_part)
        elif community:
            super().__init__(
                driver,
                xpath="//*[@content-desc='chat-name-text'][starts-with(@text,'%s')]/.." % username_part)
        else:
            super().__init__(
                driver,
                xpath="//*[@content-desc='author-primary-name'][starts-with(@text,'%s')]/.." % username_part)

    def navigate(self):
        if self.community:
            from views.chat_view import CommunityView
            return CommunityView(self.driver)
        else:
            from views.chat_view import ChatView
            return ChatView(self.driver)

    def click(self):
        if self.community:
            from views.chat_view import CommunityView
            desired_element = CommunityView(self.driver).community_title
        else:
            from views.chat_view import ChatView
            desired_element = ChatView(self.driver).chat_message_input
        self.click_until_presence_of_element(desired_element=desired_element)

        return self.navigate()

    def find_element(self):
        for i in range(2):
            try:
                return super(ChatElement, self).find_element()
            except NoSuchElementException as e:
                if i == 0:
                    self.wait_for_visibility_of_element(20)
                else:
                    e.msg = 'Device %s: Unable to find chat with name %s' % (self.driver.number, self.username)
                    raise e

    @property
    def new_messages_counter(self):
        if self.community:
            return UnreadMessagesCountText(self.driver, self.locator)

        class NewMessageCounterText(Text):
            def __init__(self, driver, parent_locator: str):
                super().__init__(
                    driver,
                    xpath="%s//*[@content-desc='new-message-counter']/android.widget.TextView" % parent_locator)

        return NewMessageCounterText(self.driver, self.locator)

    @property
    def chat_preview(self):
        class PreveiewMessageText(Text):
            def __init__(self, driver, parent_locator: str):
                super().__init__(driver, xpath="%s//*[@content-desc='chat-message-text']" % parent_locator)

        return PreveiewMessageText(self.driver, self.locator)

    @property
    def no_message_preview(self):
        class NoMessageText(Text):
            def __init__(self, driver, parent_locator: str):
                super().__init__(driver, xpath="%s//*[@content-desc='no-messages-text']" % parent_locator)

        return NoMessageText(self.driver, self.locator)

    @property
    def new_messages_grey_dot(self):
        class UnreadMessagesPublicChat(BaseElement):
            def __init__(self, driver, parent_locator):
                super().__init__(driver, xpath="%s/*[@content-desc='unviewed-messages-public']" % parent_locator)

        return UnreadMessagesPublicChat(self.driver, self.locator)

    @property
    def chat_image(self):
        return BaseElement(self.driver, xpath=self.locator + "//*[@content-desc='profile-picture']")

    @property
    def profile_unblock_button(self):
        return Button(self.driver, xpath=self.locator + "/*[@content-desc='Unblock']")


class ActivityCenterElement(SilentButton):
    def __init__(self, driver, username):
        self.chat_name = username
        super().__init__(driver,
                         xpath="//*[contains(@text, '%s')]/ancestor::*[@content-desc='activity']" % username)

    @property
    def title(self):
        return Text(self.driver, xpath=self.locator + '//*[@content-desc="activity-title"]')

    @property
    def unread_indicator(self):
        return Button(self.driver, xpath=self.locator + '//*[@content-desc="activity-unread-indicator"]')

    @property
    def message_body(self):
        return Button(self.driver, xpath=self.locator + '//*[@content-desc="activity-message-body"]')

    @property
    def context_tag_text(self):
        return Text(self.driver, xpath=self.locator + '//*[@content-desc="context-tag"]/android.widget.TextView').text

    @property
    def pending_status_tag(self):
        return Text(self.driver, xpath=self.locator + '//*[@content-desc="status-tag-pending"]')

    @property
    def review_pairing_request_button(self):
        return Button(self.driver, accessibility_id='Review pairing request')

    @property
    def more_details_button(self):
        return Button(self.driver, accessibility_id='More details')

    def handle_cr(self, element_accessibility: str):
        Button(
            self.driver,
            xpath=self.locator + '/*[@content-desc="%s"]' % element_accessibility
        ).wait_for_rendering_ended_and_click()

    def accept_contact_request(self):
        self.handle_cr("accept-contact-request")

    def decline_contact_request(self):
        self.handle_cr("decline-contact-request")

    def cancel_contact_request(self):
        self.handle_cr("cancel-contact-request")


class PushNotificationElement(SilentButton):
    def __init__(self, driver, pn_text):
        self.pn_text = pn_text
        super().__init__(driver, xpath="//*[@text='%s']" % pn_text)

    @property
    def icon(self):
        class PnIconElement(BaseElement):
            def __init__(self, driver, parent_locator):
                super().__init__(driver,
                                 xpath="%s/../../../../*/*[@resource-id='android:id/message_icon']" % parent_locator)

        return PnIconElement(self.driver, self.locator)

    @property
    def username(self):
        class PnUsername(BaseElement):
            def __init__(self, driver, parent_locator):
                super().__init__(driver,
                                 xpath="%s/../../*[@resource-id='android:id/message_name']" % parent_locator)

        return PnUsername(self.driver, self.locator).text

    @property
    def group_chat_icon(self):
        class GroupChatIconElement(BaseElement):
            def __init__(self, driver, parent_locator):
                super().__init__(driver,
                                 xpath="%s/../../../../*[@resource-id='android:id/right_icon_container']" % parent_locator)

        return GroupChatIconElement(self.driver, self.locator)


class ContactDetailsRow(BaseElement):
    def __init__(self, driver, username=None, index=None):
        main_locator = "//*[@content-desc='user-list']"
        if username:
            xpath_locator = "%s[*[contains(@text,'%s')]]" % (main_locator, username)
        elif index:
            xpath_locator = "%s[%s]" % (main_locator, index)
        else:
            xpath_locator = main_locator
        super().__init__(driver, xpath=xpath_locator)

        self.options_button = Button(self.driver, xpath="(%s//android.widget.ImageView)[2]" % xpath_locator)
        self.username_text = Text(self.driver, xpath="(%s//android.widget.TextView)[2]" % xpath_locator)


class MuteButton(Button):
    def __init__(self, driver, accessibility_id):
        super().__init__(driver=driver, accessibility_id=accessibility_id)

    @property
    def text(self):
        return self.find_element().find_element(by=MobileBy.CLASS_NAME, value="android.widget.TextView").text

    @property
    def unmute_caption_text(self):
        return self.find_element().find_element(by=MobileBy.XPATH, value="//android.widget.TextView[2]").text


class ShareQRCodeInfoText(Text):
    def __init__(self, driver):
        super().__init__(driver, accessibility_id="share-qr-code-info-text")

    @property
    def text(self):
        return self.find_element().find_element(by=MobileBy.XPATH, value="/android.widget.TextView").text


class HomeView(BaseView):
    def __init__(self, driver):
        super().__init__(driver)

        self.skip_editing_profile_button = Button(self.driver, xpath="//*[@content-desc='button-two']//*[@text='Skip']")
        self.edit_profile_button = Button(self.driver, xpath="//*[@content-desc='button-one']//*[@text='Edit Profile']")
        self.submit_create_profile_button = Button(self.driver, accessibility_id="submit-create-profile-button")
        self.plus_community_button = Button(self.driver, accessibility_id="new-communities-button")
        self.chat_name_text = Text(self.driver, accessibility_id="chat-name-text")
        self.start_new_chat_button = ChatButton(self.driver, accessibility_id="start-1-1-chat-button")
        self.new_group_chat_button = ChatButton(self.driver, accessibility_id="start-group-chat-button")
        self.join_public_chat_button = ChatButton(self.driver, accessibility_id="join-public-chat-button")
        self.universal_qr_scanner_button = Button(self.driver, accessibility_id="universal-qr-scanner")
        self.invite_friends_button = Button(self.driver, accessibility_id="invite-friends-button")
        self.stop_status_service_button = Button(self.driver, accessibility_id="STOP")
        self.my_profile_on_start_new_chat_button = Button(self.driver,
                                                          xpath="//*[@content-desc='current-account-photo']")
        self.communities_button = ChatButton(self.driver, accessibility_id="create-community")
        self.create_closed_community_button = ChatButton(self.driver, accessibility_id="create-closed-community")
        self.create_open_community_button = ChatButton(self.driver, accessibility_id="create-open-community")
        self.create_token_gated_community_button = ChatButton(self.driver,
                                                              accessibility_id="create-token-gated-community")
        self.ens_banner_close_button = Button(self.driver, accessibility_id=":ens-banner-close-button")
        self.user_name_text = Text(
            self.driver, xpath="//*[@text='User found']/following-sibling::*/android.widget.TextView[1]")

        # Notification centre
        self.notifications_button = Button(self.driver, accessibility_id="notifications-button")
        self.notifications_unread_badge = BaseElement(
            self.driver, xpath="//*[@content-desc='activity-center-unread-count']/android.widget.TextView")
        self.open_activity_center_button = Button(self.driver, accessibility_id="open-activity-center-button")
        self.close_activity_centre = Button(self.driver, accessibility_id="close-activity-center")

        self.notifications_select_button = Button(self.driver, translation_id="select")
        self.notifications_reject_and_delete_button = Button(self.driver, accessibility_id="reject-and-delete"
                                                                                           "-activity-center")
        self.notifications_accept_and_add_button = Button(self.driver,
                                                          accessibility_id="accept-and-add-activity-center")
        self.notifications_select_all = Button(self.driver, xpath="(//android.widget.CheckBox["
                                                                  "@content-desc='checkbox-off'])[1]")

        # Tabs and elements on messages home view
        self.recent_tab = Button(self.driver, accessibility_id="tab-recent")
        self.groups_tab = Button(self.driver, accessibility_id="tab-groups")
        self.contacts_tab = Button(self.driver, accessibility_id="tab-contacts")
        self.contact_new_badge = Button(self.driver, accessibility_id="notification-dot")
        self.pending_contact_request_button = Button(self.driver,
                                                     accessibility_id="open-activity-center-contact-requests")
        self.pending_contact_request_text = Text(
            self.driver,
            xpath='//*[@content-desc="pending-contact-requests-count"]/android.widget.TextView')

        # Tabs and elements on community home view
        self.pending_communities_tab = Button(self.driver, accessibility_id="pending-tab")
        self.joined_communities_tab = Button(self.driver, accessibility_id="joined-tab")
        self.opened_communities_tab = Button(self.driver, accessibility_id="opened-tab")

        # Options on long tap
        self.chats_menu_invite_friends_button = Button(self.driver, accessibility_id="chats-menu-invite-friends-button")
        self.close_chat_button = Button(self.driver, accessibility_id="close-chat")
        self.confirm_closing_chat_button = Button(self.driver, accessibility_id="Confirm")
        self.clear_history_button = Button(self.driver, accessibility_id="clear-history")
        self.mute_chat_button = MuteButton(self.driver, accessibility_id="mute-chat")
        self.mute_community_button = MuteButton(self.driver, accessibility_id="mute-community")
        self.unmute_community_button = MuteButton(self.driver, accessibility_id="unmute-community")
        self.mute_channel_button = MuteButton(self.driver, accessibility_id="chat-toggle-muted")
        self.mark_all_messages_as_read_button = Button(self.driver, accessibility_id="mark-as-read")

        # Connection icons
        self.connection_offline_icon = Button(self.driver, accessibility_id="conn-button-offline")
        self.new_chat_button = Button(self.driver, accessibility_id="new-chat-button")
        self.discover_communities_button = Button(self.driver, accessibility_id="communities-home-discover-card")

        # Bottom sheet
        self.start_a_new_chat_bottom_sheet_button = Button(self.driver, accessibility_id="start-a-new-chat")
        self.add_a_contact_chat_bottom_sheet_button = Button(self.driver, accessibility_id="add-a-contact")

        # Activity centre
        self.all_activity_tab_button = ActivityTabButton(self.driver, translation_id="all")
        self.mention_activity_tab_button = ActivityTabButton(self.driver, accessibility_id="tab-mention")
        self.reply_activity_tab_button = ActivityTabButton(self.driver, accessibility_id="tab-reply")
        self.activity_notification_swipe_button = Button(self.driver, accessibility_id="notification-swipe")
        self.activity_unread_filter_button = Button(self.driver, accessibility_id="selector-filter")
        self.more_options_activity_button = Button(self.driver, accessibility_id="activity-center-open-more")
        self.mark_all_read_activity_button = Button(self.driver, translation_id="mark-all-notifications-as-read")

        # Share tab
        self.share_qr_code_info_text = ShareQRCodeInfoText(self.driver)
        self.link_to_profile_button = Button(self.driver, accessibility_id="link-to-profile")
        self.link_to_profile_text = Text(self.driver, accessibility_id="share-qr-code-info-text")
        self.close_share_tab_button = Button(self.driver, accessibility_id="close-shell-share-tab")
        self.qr_code_image_element = BaseElement(self.driver, accessibility_id='share-qr-code')
        self.share_profile_tab_button = Button(self.driver, accessibility_id="Profile")
        self.share_wallet_tab_button = Button(self.driver, accessibility_id="Wallet")
        self.account_avatar = BaseElement(self.driver, accessibility_id="account-avatar")
        self.account_name_text = Text(
            self.driver, xpath="//*[@content-desc='link-to-profile']/preceding-sibling::android.widget.TextView")
        self.share_link_to_profile_button = Button(self.driver, accessibility_id='link-to-profile')
        # Discover communities
        self.community_card_item = BaseElement(self.driver, accessibility_id="community-card-item")

    def get_activity_center_element_by_text(self, text_part):
        return ActivityCenterElement(self.driver, text_part)

    def get_chat(self, username, community=False, community_channel=False, wait_time=10):
        if community:
            self.driver.info("Looking for community: '%s'" % username)
        else:
            self.driver.info("Looking for chat: '%s'" % username)
        chat_element = ChatElement(self.driver, username[:25], community=community, community_channel=community_channel)
        if not chat_element.is_element_displayed(wait_time) and community is False and community_channel is False:
            if self.notifications_unread_badge.is_element_displayed(30):
                chat_in_ac = self.get_activity_center_element_by_text(username[:25])
                self.open_activity_center_button.click_until_presence_of_element(chat_in_ac)
                chat_in_ac.wait_for_element(20)
                chat_in_ac.click()
        return chat_element

    def get_to_community_channel_from_home(self, community_name, channel_name='general'):
        community_view = self.get_community_view()
        self.get_chat(community_name, community=True).click()
        return community_view.get_channel(channel_name).click()

    def get_chat_from_home_view(self, username):
        self.driver.info("Looking for chat: '%s'" % username)
        chat_element = ChatElement(self.driver, username[:25])
        return chat_element

    def get_element_from_activity_center_view(self, message_body):
        self.driver.info("Looking for activity center element: '%s'" % message_body)
        chat_element = self.get_activity_center_element_by_text(message_body)
        return chat_element

    def handle_contact_request(self, username: str, action: str = 'accept', notifications_count: int = 1):
        if self.toast_content_element.is_element_displayed(10):
            self.toast_content_element.wait_for_invisibility_of_element()
        try:
            self.notifications_unread_badge.wait_for_visibility_of_element(seconds=120)
        except TimeoutException:
            pass
        if notifications_count > 1:
            try:
                self.notifications_unread_badge.wait_for_element_text(text=str(notifications_count), wait_time=120)
            except TimeoutException:
                pass
        self.open_activity_center_button.click_until_presence_of_element(self.close_activity_centre)
        chat_element = self.get_activity_center_element_by_text(username[:25])
        try:
            if action == 'accept':
                self.driver.info("Accepting incoming CR for %s" % username)
                chat_element.accept_contact_request()
            elif action == 'decline':
                self.driver.info("Rejecting incoming CR for %s" % username)
                chat_element.decline_contact_request()
            elif action == 'cancel':
                self.driver.info("Canceling outgoing CR for %s" % username)
                chat_element.cancel_contact_request()
            else:
                self.driver.fail("Illegal option for CR!")
        except (NoSuchElementException, TimeoutException):
            self.driver.fail("No contact request received from %s" % username)
        finally:
            self.close_activity_centre.wait_for_rendering_ended_and_click()
            self.chats_tab.wait_for_visibility_of_element()

    def add_contact(self, public_key, username='', nickname='', close_profile=True):
        self.driver.info("Adding user to Contacts via chats > add new contact")
        self.new_chat_button.click()
        if username:
            self.edit_profile_button.click()
            self.get_sign_in_view().profile_title_input.send_keys(username)
            self.submit_create_profile_button.click()
        else:
            self.skip_editing_profile_button.click_if_shown()
        self.add_a_contact_chat_bottom_sheet_button.click()

        chat = self.get_chat_view()
        chat.public_key_edit_box.click()
        chat.public_key_edit_box.send_keys(public_key)
        chat.element_by_translation_id("user-found").wait_for_visibility_of_element()
        if not chat.view_profile_new_contact_button.is_element_displayed():
            chat.click_system_back_button()
        chat.view_profile_new_contact_button.click_until_presence_of_element(chat.profile_send_contact_request_button)
        chat.profile_send_contact_request_button.click()
        chat.contact_request_message_input.send_keys("hi")
        chat.confirm_send_contact_request_button.click()
        if nickname:
            chat.set_nickname(nickname)
        if close_profile:
            chat.close_button.click_until_absense_of_element(chat.close_button)

    def create_group_chat(self, user_names_to_add: list, group_chat_name: str = 'new_group_chat'):
        self.driver.info("## Creating group chat '%s'" % group_chat_name, device=False)
        self.new_chat_button.click()
        chat = self.get_chat_view()
        self.start_a_new_chat_bottom_sheet_button.click()
        for user_name in user_names_to_add:
            check_box = chat.get_username_checkbox(user_name)
            if not check_box.is_element_displayed():
                raise NoSuchElementException(
                    "User with the name '%s' is not in contacts list so can't create a group chat" % user_name)
            check_box.click_until_presence_of_element(chat.get_username_checkbox(user_name, state_on=True))
        self.next_button.click()
        self.get_sign_in_view().profile_title_input.send_keys(group_chat_name)
        chat.create_group_chat_button.click()
        self.driver.info("## Group chat %s is created successfully!" % group_chat_name, device=False)
        return chat

    def create_community(self, community_type: Literal["open", "closed", "token-gated"]):
        self.driver.info("## Creating %s community" % community_type)
        self.plus_community_button.click()
        if community_type == "open":
            self.create_open_community_button.click()
        elif community_type == "closed":
            self.create_closed_community_button.click()
        elif community_type == "token-gated":
            self.create_token_gated_community_button.click()
        else:
            raise ValueError("Incorrect community type is set")

    def delete_chat_long_press(self, username):
        self.driver.info("Deleting chat '%s' by long press" % username)
        self.get_chat(username).long_press_without_release()
        self.close_chat_button.double_click()
        self.confirm_closing_chat_button.click()

    def clear_chat_long_press(self, username):
        self.driver.info("Clearing history in chat '%s' by long press" % username)
        self.get_chat(username).long_press_element()
        self.clear_history_button.click()
        from views.chat_view import ChatView
        ChatView(self.driver).clear_button.click()

    def mute_chat_long_press(self, chat_name, mute_period="mute-till-unmute", community=False, community_channel=False):
        self.driver.info("Muting chat with %s" % chat_name)
        self.get_chat(username=chat_name, community=community,
                      community_channel=community_channel).long_press_without_release()
        if community:
            element = self.mute_community_button
        elif community_channel:
            element = self.mute_channel_button
        else:
            element = self.mute_chat_button
        element.double_click()
        self.element_by_translation_id(mute_period).click()

    def get_pn(self, pn_text: str, wait_time: int = 60):
        self.driver.info("Getting PN by '%s'" % pn_text)
        expected_element = PushNotificationElement(self.driver, pn_text)
        return expected_element if expected_element.is_element_displayed(wait_time) else False

    def contact_details_row(self, username=None, index=None):
        return ContactDetailsRow(self.driver, username=username, index=index)

    def get_contact_rows_count(self):
        return len(ContactDetailsRow(self.driver).find_elements())

    def get_link_to_profile(self):
        self.driver.info('Getting profile link via share profile QR')
        self.show_qr_code_button.click()
        self.share_profile_tab_button.click()
        self.link_to_profile_text.click()
        link_to_profile = self.driver.get_clipboard_text()
        if not link_to_profile:
            raise NoSuchElementException("Can't get link to profile")
        return link_to_profile

    def get_public_key(self):
        self.driver.info("Getting public key via Share tab")
        link_to_profile = self.get_link_to_profile()
        self.click_system_back_button()
        return link_to_profile.split("#")[-1]

    def copy_wallet_address(self):
        self.share_link_to_profile_button.click()
        address = self.sharing_text_native.text
        self.click_system_back_button()
        return address

    def get_discover_community_card_by_name(self, community_name: str):
        return BaseElement(
            self.driver,
            xpath="//*[@content-desc='%s'][descendant::*[@content-desc='chat-name-text'][@text='%s']]" % (
                self.community_card_item.accessibility_id, community_name)
        )

    def get_new_device_installation_id(self):
        element = Text(self.driver, accessibility_id='new-device-installation-id')
        element.wait_for_visibility_of_element()
        return element.text

    def get_username(self):
        self.toast_content_element.wait_for_invisibility_of_element()
        profile_view = self.get_profile_view()
        self.profile_button.click_until_presence_of_element(profile_view.default_username_text)
        profile_view.default_username_text.wait_for_element(3)
        username = profile_view.default_username_text.text
        profile_view.click_system_back_button()
        return username
